﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GameDataTempshitEditor.DataEditors
{
    public interface IDataObjectEditor
    {
        Red.GameData.Raw.DataObject GetObject();
        Red.Core.UndoRedo.ActionHistory GetActionHistory();
        void RefreshValues();
    }

    public class DataEditors
    {
        private readonly TabControl _Tabs;
        private readonly Red.GameData.EditorEnvironment _Env;

        //---------------------

        public delegate void ObjectNameChangedHandler(object sender, ObjectChangeEventArgs e);
        public delegate void EditorChanged(object sender);
        public event ObjectNameChangedHandler OnObjectChanged;
        public event EditorChanged OnEditorChanged;

        //---------------------

        public DataEditors( TabControl tabs, Red.GameData.EditorEnvironment env )
        {
            _Env = env;
            _Tabs = tabs;

            _Tabs.MouseUp += tabs_MouseUp;
            _Tabs.Selected += tabs_Selected;
        }

        /// <summary>
        /// Get action history related to current editor
        /// </summary>
        public Red.Core.UndoRedo.ActionHistory ActionHistory
        {
            get
            {
                try
                {
                    var selectedPage = _Tabs.SelectedTab;
                    if (null != selectedPage)
                    {
                        var dataInterface = (IDataObjectEditor)selectedPage.Controls[0];
                        return dataInterface.GetActionHistory();
                    }
                }
                catch (InvalidCastException)
                { }
                catch (NullReferenceException)
                { }

                return null;
            }
        }

        public void CloseAllEditors()
        {
            _Tabs.TabPages.Clear();

            if (OnEditorChanged != null)
                OnEditorChanged(this);
        }

        public bool CloseEditorForObject(Red.GameData.Raw.DataObject obj)
        {
            // refresh tab captions
            foreach (TabPage tab in _Tabs.TabPages)
            {
                try
                {
                    var dataInterface = (IDataObjectEditor)tab.Controls[0];
                    var dataObject = dataInterface.GetObject();

                    // refresh tab name
                    if ( dataObject == obj )
                    {
                        _Tabs.TabPages.Remove(tab);
                        return true;
                    }
                }
                catch (InvalidCastException)
                { }
                catch (NullReferenceException)
                { }
            }          

            // nothing removed
            return false;
        }

        public void RefreshNames()
        {
            // refresh tab captions
            foreach (TabPage tab in _Tabs.TabPages)
            {
                try
                {
                    var dataInterface = (IDataObjectEditor)tab.Controls[0];
                    var dataObject = dataInterface.GetObject();

                    // refresh tab name
                    tab.Text = Red.GameData.Raw.Utils.GetObjectNameSafe(_Env.Reflection, dataObject);
                }
                catch (InvalidCastException)
                {}
                catch (NullReferenceException)
                {}
            }            
        }

        public void OpenEditor( Red.GameData.Raw.DataObjectView obj )
        {
            // find existing
            foreach ( TabPage tab in _Tabs.TabPages )
            {
                try
                {
                    var dataInterface = (IDataObjectEditor)tab.Controls[0];
                    if ( dataInterface.GetObject() == obj.RawData )
                    {
                        _Tabs.SelectedTab = tab;

                        if (OnEditorChanged != null)
                            OnEditorChanged(this);

                        return;
                    }
                }
                catch (InvalidCastException)
                {}
                catch (NullReferenceException)
                {}
            }

            // create new editor
            try
            {
                var newEditor = CreateEditor(obj.RawData);
                newEditor.Dock = DockStyle.Fill;

                var tabName = Red.GameData.Raw.Utils.GetObjectNameSafe(_Env.Reflection, obj.RawData);
                TabPage newTab = new TabPage(tabName);
                newTab.Controls.Add(newEditor);

                _Tabs.TabPages.Add(newTab);
                _Tabs.SelectedTab = newTab;

                if (OnEditorChanged != null)
                    OnEditorChanged(this);
            }
            catch (Exception e)
            {
                ErrorBox.ShowError(String.Format("Failed to open editor for object {0}, class {1}: {2}", obj.Name, obj.ClassName, e.ToString()));
            }            
        }

        private Control CreateEditor(Red.GameData.Raw.DataObject obj)
        {
            // TODO: this would require a proper editor factory so we don't end up with a IF ladder
            // 

            // create the edition context
            var context = _Env.CreateContext();
            if (context == null)
                return null;

            // create the root view of the object
            var editorView = Red.GameData.EditorObjectView.CreateView(context, obj);
            if (editorView == null)
            {
                context.Dispose();
                return null;
            }

            // fall back to property grid as default
            {
                var editor = new DataEditor_PropertyGrid(context, editorView);
                editor.OnObjectChanged += editor_OnObjectChanged;
                return editor;
            }
        }

        // object properties has changed
        void editor_OnObjectChanged(object sender, ObjectChangeEventArgs e)
        {
            // TODO: filter scope of this change
            RefreshNames();

            // propagate
            if (OnObjectChanged != null)
                OnObjectChanged(this, e);
        }

        // closing pages        
        private void tabs_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Middle)
            {
                //Looping through the controls.
                for (int i = 0; i < _Tabs.TabPages.Count; i++)
                {
                    Rectangle r = _Tabs.GetTabRect(i);
                    if (r.Contains(e.Location))
                    {
                       // if (MessageBoxEx.Show(_Tabs, "Would you like to Close this Tab?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
                        {
                            _Tabs.TabPages.RemoveAt(i);

                            if (OnEditorChanged != null)
                                OnEditorChanged(this);

                            break;
                        }
                    }
                }
            }
        }

        private void tabs_Selected(object sender, TabControlEventArgs e)
        {
            var selectedPage = _Tabs.SelectedTab;
            if (null != selectedPage)
            {
                var dataInterface = (IDataObjectEditor)selectedPage.Controls[0];
                dataInterface.RefreshValues();

                if (OnEditorChanged != null)
                    OnEditorChanged(this);
            }
        }     
    }

    // name changing
    public class ObjectChangeEventArgs : EventArgs
    {
        public Red.GameData.Raw.DataObject Object { get; private set; }
        public Red.GameData.Raw.ReflectionClass Class { get; private set; }

        public ObjectChangeEventArgs(Red.GameData.Raw.DataObject changedObject, Red.GameData.Raw.ReflectionClass objectClass)
        {
            Object = changedObject;
            Class = objectClass;
        }
    }

}
